package gggameserver.testbed;

import gggameserver.compiler.GGSolutionData;
import gggameserver.exceptions.GGProcessException;
import gggameserver.util.GGConfiguration;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.StringTokenizer;
import java.util.TreeMap;

import org.apache.log4j.Logger;

public class GGTestBedFactory {

    private static org.apache.log4j.Logger log = Logger.getLogger(GGTestBedFactory.class);

	private String dir;
	private File dir_f;
	private boolean enableRemote = false;
	private ServerSocket server = null;
	
	private TreeMap<String, GGMirror> mirrors = new TreeMap<String, GGMirror>();
	
	public static GGTestBedFactory createTestBedFactory() {
		return new GGTestBedFactory(GGConfiguration.getProperty("testbed.dir"));
	}
	
	public GGTestBedFactory(String dir) {
		this.dir = dir;
		this.dir_f = new File(this.dir);
		
		if (!this.dir_f.exists()) 
			throw new IllegalStateException("Test-bed directory '" + this.dir + "' does not exist.");

		StringTokenizer st = new StringTokenizer(GGConfiguration.getProperty("m-testbed.list"), " ,");
		while (st.hasMoreTokens()) {
			String mirrorName = st.nextToken();
			if (mirrorName.isEmpty()) continue;
			
			mirrors.put(mirrorName, new GGMirror(mirrorName));
		}
	}
	
	public GGMirror getMirror(String name) {
System.out.println(name);
		return mirrors.get(name).getCopy();
	}
	
	public void enableRemoteTestBed(String hostname, int port) throws IOException {
		log.info("Enabling remote testbed with hostname=''" + hostname +
				" and port=" + port + "...");
		InetSocketAddress adr = new InetSocketAddress(hostname, port);
		server = new ServerSocket();
		server.bind(adr);
		enableRemote = true;
		log.info("Remote testbed enabled successfully.");
	}
	
	public void initializeLocalTestBed(GGLocalTestBed testbed, InputStream data) throws IOException {
		String prefix = ".tmp";
		File tmp_file = File.createTempFile("testbed_", prefix, this.dir_f);
		String dir_name = tmp_file.getName();
		dir_name = dir_name.substring(0, dir_name.length() - prefix.length());
		log.debug("Creating test-bed folder '" + dir_name + "' into '" + this.dir_f.getCanonicalPath() + "'");
		File tmp_dir = new File(this.dir_f, dir_name);
		if (!tmp_dir.mkdirs())
			throw new IOException("Could not make test-bed folder '" + dir_name + "' into '" + this.dir_f.getCanonicalPath() + "'");
		tmp_file.delete();
		testbed.initialize(data, tmp_dir);
	}
	
	public GGTestBed prepareTestBed(GGSolutionData sol) {
		if (enableRemote) return prepareRemoteTestBed(sol);
		else return prepareLocalTestBed(sol);
	}
	
	public GGTestBed prepareRemoteTestBed(GGSolutionData sol) {
		GGTestBed testbed = null;
		do {
			// TODO try new testbed
			try {
				Socket socket = server.accept();
				testbed = new GGRemoteTestBed(socket, sol);
			} catch (IOException e) {
				log.warn("Error while accepting socket", e);
			}
		} while (null == testbed);
		return testbed;
	}

	public GGMirroredTestBed prepareMirroredTestBed(String mirrorName, GGSolutionData sol, int nr) {
		GGLocalTestBed localTB = prepareLocalTestBed(sol, false);

System.out.println("Initializing mirror");
		GGMirroredTestBed tb = new GGMirroredTestBed(mirrorName, localTB);
		try {
			tb.initialize(this, sol.getLanguage(), nr);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			tb.setInvalid(e);
		}
		
		return tb;
	}
	
	public GGLocalTestBed prepareLocalTestBed(GGSolutionData sol) {
		return prepareLocalTestBed(sol, true);
	}
	
	public GGLocalTestBed prepareLocalTestBed(GGSolutionData sol, boolean compile) {
		GGLocalTestBed tb = new GGLocalTestBed();
		try {
			this.initializeLocalTestBed(tb, sol.getData());
			tb.unpack();
			if (compile) {
				//GGCompiler compiler = 
					tb.compile(sol.getLanguage());
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			log.fatal("Error while creating test-pad", e);
			tb.setInvalid(e);
		} catch (GGProcessException e) {
			log.fatal(e.toString());
			tb.setInvalid(e);
		}
		return tb;
	}

	public GGTestBed prepareLocalTestTestBed(File dir, String language) {
		// TODO Auto-generated method stub
		GGLocalTestBed tb = new GGLocalTestBed(dir);
		try {
			//GGCompiler compiler = 
				tb.compile(language);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			log.fatal("Error while creating test-pad", e);
			tb.setInvalid(e);
		} catch (GGProcessException e) {
			log.fatal(e.toString());
			tb.setInvalid(e);
		}
		return tb;
	}

	public GGTestBed prepareLocalCompiledTestBed(File dir, String language) {
		// TODO Auto-generated method stub
		GGLocalTestBed tb = new GGLocalTestBed(dir);
		try {
			tb.setLanguage(language);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			log.fatal("Error while creating test-pad", e);
			tb.setInvalid(e);
		} catch (GGProcessException e) {
			log.fatal(e.toString());
			tb.setInvalid(e);
		}
		return tb;
	}

}
